package com.ccc.boot.generator.util;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.ccc.boot.generator.dto.SettingEntity;
import com.ccc.boot.generator.dto.TableDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * MyBatis-plus代码生成器 实现工具类
 *
 * @author superC
 * @date 2022/3/29
 * @version 0.1.0
 */

public class GenerateUtils {

    protected static final Logger logger = LoggerFactory.getLogger(GenerateUtils.class);

    /**
     * 工作目录(user.home：用户的主目录；user.dir：用户的当前工作目录)
     */
    private static final String USER_DIR = System.getProperty("user.dir");

    /**
     * 包配置 - 实体类目录
     */
    private static final String PACKAGE_ENTITY = "entity";

    /**
     * 包配置 - 数据访问接口目录
     */
    private static final String PACKAGE_MAPPER = "mapper";

    /**
     * 包配置 - 业务处理接口目录
     */
    private static final String PACKAGE_SERVICE = "service";

    /**
     * 包配置 - 业务处理实现目录
     */
    private static final String PACKAGE_SERVICE_IMPL = "service.impl";

    /**
     * 包配置 - 控制器目录
     */
    private static final String PACKAGE_CONTROLLER = "controller";

    /**
     * 通用代码类的路径（BaseDO、BaseController...）
     */
    private static final String BASE_PACKAGE = "com.ccc.boot.base";


    /**
     * 生成代码
     *
     * @param outputDirPre      生成文件的输出目录前缀
     * @param settingEntity
     * @param dataSourceConfig
     * @author superC
     * @date 2022/3/29 16:22
     */
    public static void generateCode(String outputDirPre, SettingEntity settingEntity, DataSourceConfig dataSourceConfig) {
        AutoGenerator generator = new AutoGenerator();

        String dirPre = StringUtils.isEmpty(outputDirPre) ? new GlobalConfig().getOutputDir() : outputDirPre;
        /**
         * 全局相关配置
         */
        generator.setGlobalConfig(globalConfig(dirPre, settingEntity.getAuthor()));
        /**
         * 数据源配置
         */
        generator.setDataSource(dataSourceConfig);
        /**
         * 包相关配置
         */
        generator.setPackageInfo(packageConfig(settingEntity));
        /**
         * 模板配置
         */
        generator.setTemplate(templateConfig());
        generator.setTemplateEngine(new FreemarkerTemplateEngine());
        /**
         * 注入配置
         */
        generator.setCfg(injectionConfig(dirPre));
        /**
         * 数据库表配置
         */
        generator.setStrategy(strategyConfig(settingEntity));

        /**
         * 生成代码
         */
        generator.execute();

        logger.warn("==========================生成代码所在路径：{}", generator.getGlobalConfig().getOutputDir());
    }

    public static void generateCode(SettingEntity settingEntity, DataSourceConfig dataSourceConfig) {
        generateCode(USER_DIR, settingEntity, dataSourceConfig);
    }

    /**
     * 全局配置
     *
     * @date 2022/3/30 13:06
     * @param preDir
     * @param author
     * @return {@link GlobalConfig}
     * @author superC
     */
    private static GlobalConfig globalConfig(String preDir, String author) {
        GlobalConfig config = new GlobalConfig();
        // 生成文件的输出目录
        config.setOutputDir(preDir + "/src/main/java");
        config.setAuthor(author);
        config.setOpen(false);
        config.setBaseResultMap(true);
        config.setBaseColumnList(true);
        return config;
    }

    /**
     * 包配置
     *
     * @return {@link PackageConfig}
     */
    private static PackageConfig packageConfig(SettingEntity settingEntity) {
        PackageConfig config = new PackageConfig();
        // 你哪个父目录下创建包
        config.setParent(settingEntity.getPackageNm());
        // 设置模块的名字，比如 core，生成效果为 com.kim.core
        config.setModuleName(settingEntity.getMainModule());
        // 实体类创建在哪个包
        config.setEntity(PACKAGE_ENTITY);
        config.setMapper(PACKAGE_MAPPER);
        config.setService(PACKAGE_SERVICE);
        config.setServiceImpl(PACKAGE_SERVICE_IMPL);
        config.setController(PACKAGE_CONTROLLER);
        return config;
    }

    /**
     * 代码生成模板配置 - Freemarker
     *
     * @return {@link TemplateConfig}
     */
    private static TemplateConfig templateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setEntity("templates/entity.java");
        templateConfig.setMapper("templates/mapper.java");
        templateConfig.setService("templates/service.java");
        templateConfig.setServiceImpl("templates/serviceImpl.java");
        templateConfig.setController("templates/controller.java");
        templateConfig.setXml(null);
        return templateConfig;
    }

    /**
     * 数据库表配置
     *
     * @return {@link StrategyConfig}
     */
    private static StrategyConfig strategyConfig(SettingEntity settingEntity) {
        // 策略配置,数据库表配置
        StrategyConfig config = new StrategyConfig();
        // 数据库表映射到实体的命名策略
        config.setNaming(NamingStrategy.underline_to_camel);
        // 数据库表字段映射到实体类的命名策略
        config.setColumnNaming(NamingStrategy.underline_to_camel);
        // 实体是否为 lombok 模型
        config.setEntityLombokModel(true);
        // 需要包含的表名
//        config.setInclude(getTables(settingEntity.getSelectTables()).split(","));
        // 需要包含的表名(直接获取表名)
        config.setInclude(settingEntity.getTableNames().trim().split(","));
        // 驼峰转连字符串
        config.setControllerMappingHyphenStyle(true);
        // REST 风格
        config.setRestControllerStyle(true);
        // 表前缀; 生成时会删除前缀，比如：core_admin 最终构建为 Admin, AdminController ...
        config.setTablePrefix(settingEntity.getTablePrefix() + "_");

        // 字段填充
        List<TableFill> tableFills = new ArrayList<>();
        tableFills.add(new TableFill("create_time", FieldFill.INSERT));
        tableFills.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
        config.setTableFillList(tableFills);

        // CommonBase
        config.setSuperEntityColumns("id", "create_time", "update_time");
        config.setSuperEntityClass(BASE_PACKAGE.concat(".BaseDO"));
        config.setSuperServiceClass(BASE_PACKAGE.concat(".IBaseService"));
        config.setSuperServiceImplClass(BASE_PACKAGE.concat(".BaseServiceImpl"));
        config.setSuperControllerClass(BASE_PACKAGE.concat(".BaseController"));

        return config;
    }

    /**
     * 要生成的表名，用 `,` 分割
     *
     * @param selectTables
     * @return
     */
    private static String getTables(List<TableDTO> selectTables) {
        StringBuilder tables = new StringBuilder();
        selectTables.forEach(tableDTO -> tables.append(tableDTO.getTableName()).append(","));
        String s = tables.toString();
        return s.substring(0, s.length() - 1);
    }

    /**
     * 注入配置
     * @param outputDirPre
     */
    private static InjectionConfig injectionConfig(String outputDirPre) {
        InjectionConfig config = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 自定义输出 mapper.xml 到 resources 目录下
        String mapperPath = "/templates/mapper.xml.ftl";
        List<FileOutConfig> focList = new ArrayList<>();

        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(mapperPath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                StringBuilder sb = new StringBuilder();
                sb.append(outputDirPre)
                        .append("/src/main/resources/mapper/")
                        .append(tableInfo.getEntityName())
                        .append("Mapper")
                        .append(StringPool.DOT_XML);
                return sb.toString();
            }
        });

        config.setFileOutConfigList(focList);
        return config;
    }

}